/*
 * main_task.c
 *
 *  Created on: 2024年8月13日
 *      Author: nwz
 */

#include <all_header.h>
extern DMA_HandleTypeDef handle_GPDMA1_Channel3;

static void main_task_run(void *arg);
static SemaphoreHandle_t sem;
static StreamBufferHandle_t stream;
static SemaphoreHandle_t flash_mtx;

#if __FPU_USED == 1
#error "uboot cannot use fpu,app may jump failed"
#endif

void main_task_start(void) {
	ASSERT(
			xTaskCreate(main_task_run,"main",MAIN_TASK_STACK_SIZE,NULL,MAIN_TASK_PRIORITY,NULL));
	vTaskStartScheduler();
}

static void init_threads(void) {
	boot_app_task_init();
	console_write_task_init();
	console_read_task_init();
	tip_task_init();
	flash_data_task_init(flash_mtx);
	flash_stream_task_init(flash_mtx);
}

static void create_threads(void) {
	ASSERT(
			xTaskCreate(led_task_run,"led",LED_TASK_STACK_SIZE,NULL,LED_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(boot_app_task_run,"boot_app",BOOT_APP_TASK_STACK_SIZE,NULL,BOOT_APP_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(console_read_task_run,"console_read",CONSOLE_READ_TASK_STACK_SIZE,NULL,CONSOLE_READ_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(console_write_task_run,"console_write",CONSOLE_WRITE_TASK_STACK_SIZE,NULL,CONSOLE_WRITE_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(flash_data_task_run,"flash_data",FLASH_DATA_TASK_STACK_SIZE,NULL,FLASH_DATA_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(flash_stream_task_run,"flash_stream",FLASH_STREAM_TASK_STACK_SIZE,NULL,FLASH_STREAM_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(tip_task_run,"tip",TIP_TASK_STACK_SIZE,NULL,TIP_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(key_task_run,"key",KEY_TASK_STACK_SIZE,NULL,KEY_TASK_PRIORITY,NULL));
	ASSERT(
			xTaskCreate(debug_task_run,"debug",DEBUG_TASK_STACK_SIZE,NULL,DEBUG_TASK_PRIORITY,NULL));

}

static void transfer_complete(DMA_HandleTypeDef *handle) {
	BaseType_t val = pdFALSE;
	ASSERT(xSemaphoreGiveFromISR(sem,&val)== pdTRUE);
}

static void init(void) {
	sem = xSemaphoreCreateBinary();
	ASSERT(sem);
	stream = xStreamBufferCreate(2048, 1);
	ASSERT(stream);
	flash_mtx = xSemaphoreCreateMutex();
	ASSERT(flash_mtx);
}

static uint8_t console_read_char() {
	uint8_t val = 0;
	ASSERT(console_read_task_read(&val,1,portMAX_DELAY) == 1);
	console_read_task_clear();
	return val;
}

static void copy_flash_callback(uint32_t size, uint32_t total_size) {

}

static void update_app(void) {
	main_task_ops_copy_app(copy_flash_callback);
}

static int check_app(void) {
	return main_task_ops_check_app();
}

static void download_app_callback(uint32_t tsize, uint32_t file_size) {
	if (tsize == 0) {
		// ready
		tip_task_set_state(TIP_TASK_STATE_DOWN_APP);
	} else {
		tip_task_set_info_no(tsize);
		tip_task_set_state(TIP_TASK_STATE_DOWNING_APP);
	}
}

static void download_bootloader_callback(uint32_t tsize, uint32_t file_size) {
	if (tsize == 0) {
		// ready
		tip_task_set_state(TIP_TASK_STATE_DOWN_BOOTLOADER);
	} else {
		tip_task_set_info_no(tsize);
		tip_task_set_state(TIP_TASK_STATE_DOWNING_BOOTLOADER);
	}
}

static inline void boot_app(void) {
	boot_app_task_notify(Boot_App_Task_Notify_State_Boot_App);
}

static void main_task_run(void *arg) {
	init();

	init_threads();
	create_threads();

	int ret = 0;
	bool wait = false;
	tip_task_set_state(TIP_TASK_STATE_STARTUP);
	if (key_task_k1_is_down()) {
		wait = true;
	} else {
		uint8_t flag = flash_data_task_get_flag();
		switch (flag & 0xF0) {
		case 0xC0:
			ret = check_app();
			if (ret == 0) {
				update_app();
				flash_data_task_set_flag(0x0);
				boot_app();
			} else {
				tip_task_set_info_no(ret);
				tip_task_set_state(TIP_TASK_STATE_UPDATE_CHECK_ERR);
				wait = true;
			}
			break;
		case 0x80:
			update_app();
			flash_data_task_set_flag(0x0);
			boot_app();
			break;
		case 0x00:
			boot_app();
			break;
		default:
			tip_task_set_state(TIP_TASK_STATE_APP_FLAG_WRONG);
			wait = true;
			break;
		}
	}

	if (wait) {
		while (true) {
			int ret = 0;
			tip_task_set_state(TIP_TASK_STATE_SHOW_CHOICES);
			uint8_t ch = console_read_char();
			uint8_t flag = flash_data_task_get_flag();
			switch (ch) {
			case '1': /// update app
				// show tip
				// set flag
				tip_task_set_state(TIP_TASK_STATE_PREPARE_APP);
				flash_data_task_set_flag(flag | 0xF0); // 0xFx start
				ret = main_task_ops_download(download_app_callback);
				if (ret > 0) {
					flash_data_task_set_flag((flag & 0x0f) | 0xC0); //0xCx need_check
					tip_task_set_state(TIP_TASK_STATE_DOWN_APP_OK);
					// start check
					ret = main_task_ops_check_app();
					if (ret) {
						tip_task_set_info_no(ret);
						tip_task_set_state(TIP_TASK_STATE_UPDATE_CHECK_ERR);
					} else {
						flash_data_task_set_flag((flag & 0x0F) | 0x80); // 0x8x check ok
						main_task_ops_copy_app(copy_flash_callback);
						// check again
						if (main_task_ops_check_app_copy() == 0) {
							flash_data_task_set_flag(flag & 0x0F); // 0x0x copy ok
							tip_task_set_state(
									TIP_TASK_STATE_UPDATE_APP_SUCCESS);
						} else {
							tip_task_set_state(TIP_TASK_STATE_FLASH_COPY_WRONG);
						}
					}
				} else if (ret == -1) {
					tip_task_set_state(TIP_TASK_STATE_APP_SIZE_TOO_BIG);
				} else if (ret == -2) {
					tip_task_set_state(TIP_TASK_STATE_APP_SIZE_TOO_SMALL);
				}
				break;
			case '2': //// update uboot
				// show tip
				// set flag
				tip_task_set_state(TIP_TASK_STATE_PREPARE_BOOTLOADER);
				flash_data_task_set_flag(flag | 0x0F); // 0xFx start
				ret = main_task_ops_download(download_bootloader_callback);
				if (ret > 0) {
					flash_data_task_set_flag((flag & 0xF0) | 0x0C); //0x?C need_check
					tip_task_set_state(TIP_TASK_STATE_DOWN_BOOTLOADER_OK);
					// start check
					ret = main_task_ops_check_bootloader();
					if (ret) {
						tip_task_set_info_no(ret);
						tip_task_set_state(TIP_TASK_STATE_UPDATE_CHECK_ERR);
					} else {
						flash_data_task_set_flag((flag & 0xF0) | 0x08); //  check ok
						main_task_ops_copy_bootloader(copy_flash_callback);
						// check again
						if (main_task_ops_check_bootloader_copy() == 0) {
							flash_data_task_set_flag(flag & 0xF0); //  copy ok
							tip_task_set_state(
									TIP_TASK_STATE_UPDATE_BOOTLOADER_SUCCESS);
						} else {
							tip_task_set_state(TIP_TASK_STATE_FLASH_COPY_WRONG);
						}
					}
				} else if (ret == -1) {
					tip_task_set_state(TIP_TASK_STATE_BOOTLOADER_SIZE_TOO_BIG);
				} else if (ret == -2) {
					tip_task_set_state(
							TIP_TASK_STATE_BOOTLOADER_SIZE_TOO_SMALL);
				}
				break;
			case '3': /// reset
				boot_app_task_notify(Boot_App_Task_Notify_State_Reset);
				break;
			case '4':
				break;
			default:
				break;
			}
		}
	}

	while (true) {
		DELAY(500);
		HAL_GPIO_TogglePin(LED_1_GPIO_Port, LED_1_Pin);
	}
}

